/*
 * Tranquil Java Integrated Development Environment
 *
 * The GNU General Public License Version 3
 *
 * Copyright (C) 2021 Autumn Lamonte
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @author Autumn Lamonte [AutumnWalksTheLake@gmail.com] ⚧ Trans Liberation Now
 * @version 1
 */
package tjide.ui;

import java.io.IOException;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.ResourceBundle;

import gjexer.TExceptionDialog;
import gjexer.TWidget;
import gjexer.event.TMouseEvent;
import gjexer.ttree.TTreeItem;
import gjexer.ttree.TTreeViewWindow;

import tjide.project.ArchiveTarget;
import tjide.project.FileTarget;
import tjide.project.Project;
import tjide.project.Target;

/**
 * ProjectWindowTreeItem is a single item in a project tree view.
 */
public class ProjectWindowTreeItem extends TTreeItem {

    /**
     * Translated strings.
     */
    private static ResourceBundle i18n = ResourceBundle.getBundle(ProjectWindowTreeItem.class.getName());

    // ------------------------------------------------------------------------
    // Variables --------------------------------------------------------------
    // ------------------------------------------------------------------------

    /**
     * The project being worked on.
     */
    private Project project;

    /**
     * Target corresponding to this list item.  When null, this is a
     * directory-only entry in the window.
     */
    private Target target;

    /**
     * Children of this item, used when this is a directory-only item and the
     * view is collapsed.  Note package private access.
     */
    List<ProjectWindowTreeItem> subItems;

    // ------------------------------------------------------------------------
    // Constructors -----------------------------------------------------------
    // ------------------------------------------------------------------------

    /**
     * Public constructor, used for the virtual root target.
     *
     * @param view root TTreeViewWindow
     * @param project the project metadata
     */
    public ProjectWindowTreeItem(final TTreeViewWindow view,
        final Project project) {

        super(view.getTreeView(), project.getName(), true);

        this.project = project;
        this.target = project.getProjectTarget();
    }

    /**
     * Public constructor.
     *
     * @param view root TTreeViewWindow
     * @param parentItem the parent item
     * @param project the project metadata
     * @param target the target
     * @param expandable if true, this target contains other targets
     */
    public ProjectWindowTreeItem(final TTreeViewWindow view,
        final ProjectWindowTreeItem parentItem,
        final Project project, final Target target, final boolean expandable) {

        super(view.getTreeView(), target.getName(), expandable);

        this.project = project;
        this.target = target;

        if (target instanceof FileTarget) {
            setText(new File(target.getName()).getName());

            ProjectWindowTreeItem item = findTreeParent((FileTarget) target);
            assert (item != null);

            if (item != this) {
                // Re-parent this item deeper into the directory tree.
                getParent().getChildren().remove(this);
                this.level = item.level + 1;
                item.getChildren().add(this);
                Collections.sort(item.getChildren());
                item.subItems = new ArrayList<ProjectWindowTreeItem>();
                for (TWidget w: item.getChildren()) {
                    item.subItems.add((ProjectWindowTreeItem) w);
                }
                return;
            }
        }

        // Normal case: add the item at this level of the tree.
        if (parentItem != null) {
            this.level = parentItem.level + 1;
            parentItem.getChildren().add(this);
            Collections.sort(parentItem.getChildren());
            parentItem.subItems = new ArrayList<ProjectWindowTreeItem>();
            for (TWidget w: parentItem.getChildren()) {
                parentItem.subItems.add((ProjectWindowTreeItem) w);
            }
        }
    }

    /**
     * Private constructor used by findTreeParent().
     *
     * @param view root TTreeViewWindow
     * @param name the item name
     * @param project the project metadata
     */
    private ProjectWindowTreeItem(final TTreeViewWindow view,
        final String name, final Project project) {

        super(view.getTreeView(), name, true);

        this.project = project;
        setExpandable(true);
        setExpanded(true);
    }

    // ------------------------------------------------------------------------
    // Event handlers ---------------------------------------------------------
    // ------------------------------------------------------------------------

    /**
     * Method that subclasses can override to handle mouse button
     * double-clicks.
     *
     * @param mouse mouse button event
     */
    @Override
    public void onMouseDoubleClick(final TMouseEvent mouse) {
        if (target instanceof FileTarget) {
            TranquilApplication app = (TranquilApplication) getApplication();
            try {
                app.findTargetEditor(project, (FileTarget) target);
            } catch (IOException e) {
                new TExceptionDialog(getApplication(), e);
            }
            return;
        }
        if (target instanceof ArchiveTarget) {
            new TargetOptionsWindow(getApplication(), target);
            return;
        }
    }

    // ------------------------------------------------------------------------
    // TTreeItem --------------------------------------------------------------
    // ------------------------------------------------------------------------

    /**
     * Called when this item is expanded or collapsed.  this.expanded will be
     * true if this item was just expanded from a mouse click or keypress.
     */
    @Override
    public void onExpand() {
        getChildren().clear();
        if (!isExpanded() || !isExpandable()) {
            // We closed the tree.  Pass the close event to the child nodes.
            if (subItems != null) {
                for (ProjectWindowTreeItem item: subItems) {
                    item.setExpanded(false);
                    item.onExpand();
                }
            }
            return;
        }

        if (subItems != null) {
            // We were expanded, bring back the child nodes.
            getChildren().addAll(subItems);
        }
    }

    // ------------------------------------------------------------------------
    // ProjectWindowTreeItem --------------------------------------------------
    // ------------------------------------------------------------------------

    /**
     * Add a new child Target to this tree item.
     *
     * @param view root TTreeViewWindow
     * @param child the child target
     */
    public void addChildTarget(final TTreeViewWindow view, final Target child) {
        ProjectWindowTreeItem item = new ProjectWindowTreeItem(view, this,
            project, child, false);
        unselect();
        view.getTreeView().setSelected(item, true);
    }

    /**
     * Get the Target corresponding to this list item.
     *
     * @return the target
     */
    public final Target getTarget() {
        return target;
    }

    /**
     * Find the correct tree parent item to add a target to, building up
     * parent paths as needed.
     *
     * @param target the target
     * @return the tree item to add the target to
     */
    private ProjectWindowTreeItem findTreeParent(final FileTarget target) {
        File file = new File(target.getName());
        File parent = file.getParentFile();
        if (parent == null) {
            // Top-level already, just add it.
            return this;
        }
        // We are not top-level, so create all of the items between the top
        // and here.
        List<File> dirs = new ArrayList<File>();
        do {
            dirs.add(parent);
            parent = parent.getParentFile();
        } while (parent != null);
        Collections.reverse(dirs);

        ProjectWindow window = (ProjectWindow) getWindow();
        ProjectWindowTreeItem root = null;
        root = (ProjectWindowTreeItem) getTreeView().getTreeRoot();

        for (File dir: dirs) {
            ProjectWindowTreeItem item = window.dirItems.get(dir);
            if (item == null) {
                // We need to create this one.
                item = new ProjectWindowTreeItem((TTreeViewWindow) getWindow(),
                    dir.getName(), project);
                item.level = root.level + 1;
                window.dirItems.put(dir, item);

                root.getChildren().add(item);
                Collections.sort(root.getChildren());
                root.subItems = new ArrayList<ProjectWindowTreeItem>();
                for (TWidget w: root.getChildren()) {
                    root.subItems.add((ProjectWindowTreeItem) w);
                }
            }
            root = item;
        }
        return root;
    }

    /**
     * Comparison operator.
     *
     * @param other another ProjectWindowTreeItem instance
     * @return difference between this.tabOrder and that.tabOrder, or
     * difference between this.z and that.z, or String.compareTo(text)
     */
    public int compareTo(final TWidget other) {
        if (!(other instanceof ProjectWindowTreeItem)) {
            return super.compareTo(other);
        }

        ProjectWindowTreeItem that = (ProjectWindowTreeItem) other;

        if ((target == null) && (that.target != null)) {
            // This is a directory, sort it before a named target.
            return -1;
        }
        if ((target != null) && (that.target == null)) {
            // This is a named target, sort it after a directory.
            return 1;
        }
        if ((target == null) && (that.target == null)) {
            // These are both directories, sort based on name.
            return this.getText().compareTo(that.getText());
        }
        assert ((target != null) && (that.target != null));

        if ((target instanceof FileTarget)
            && !(that.target instanceof FileTarget)
        ) {
            // Put files ahead of other target types
            return -1;
        }
        if (!(target instanceof FileTarget)
            && (that.target instanceof FileTarget)
        ) {
            // Put other targets behind files
            return 1;
        }

        // These are both targets of a similar type, sort based on name.
        return this.getText().compareTo(that.getText());
    }

    /**
     * Delete this item from the tree.  Note package private access.
     */
    void deleteItem() {
        getParent().getChildren().remove(this);
        if (getParent() instanceof ProjectWindowTreeItem) {
            ProjectWindowTreeItem item = (ProjectWindowTreeItem) getParent();
            item.subItems.remove(this);
        }
    }

}
